package com.simemg.reader.utils;

import com.simemg.reader.store.Book;
import com.simemg.reader.store.Page;
import com.simemg.reader.store.Spider;
import ohos.utils.zson.ZSONArray;
import ohos.utils.zson.ZSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SpiderUtil {
    private final static String UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.67";

    public static Map<Integer, SpiderUtil> spiderUtilMap = new HashMap<>();
    private final Spider spider;

    public SpiderUtil(Spider spider) {
        this.spider = spider;
    }

    public static void init() {
        LogUtil.info("爬虫初始化");
        List<Spider> spiders = StoreUtil.getAllSpider();
        if (spiders.size() == 0) {
            LogUtil.info("本地爬虫库为空，拉取线上爬虫配置");
            RequestUtil
                    .Get("http://simemg.com/spider.json")
                    .onSuccess(StoreUtil::batchInsertSpider)
                    .execute();
        }
        else {
            LogUtil.info("本地爬虫数为:"+spiders.size());
            for (Spider spider : spiders) {
                spiderUtilMap.put(spider.getSpiderID(), new SpiderUtil(spider));
            }
        }
    }

    /**
     * 生成搜书事件
     * @param name 书名
     * @return 爬取事件
     */
    public static List<RequestUtil.DoSomething> getSearchBooks(String name) {
        List<RequestUtil.DoSomething> doSomethings = new ArrayList<>();
        for (SpiderUtil spiderUtil : spiderUtilMap.values()) {
            doSomethings.add(()-> {
                Object result = null;
                try {
                    result = spiderUtil.searchBooks(name);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return result;
            });
        }
        return doSomethings;
    }

    /**
     * 生成更新书列表事件
     * @param bookList 书列表
     * @return 爬取事件
     */
    public static List<RequestUtil.DoSomething> getSearchBookInfo(List<Book> bookList) {
        List<RequestUtil.DoSomething> doSomethings = new ArrayList<>();
        for (Book book:bookList) {
            SpiderUtil spiderUtil = spiderUtilMap.get(book.getSpiderID());
            if (book.getInfo() == null || book.getImg() == null)
                doSomethings.add(()-> {
                    Object result = null;
                    try {
                        result = spiderUtil.searchBookInfo(book);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return result;
                });
        }
        return doSomethings;
    }

    /**
     * 生成搜章节事件
     * @param book 书
     * @return 爬取事件
     */
    public static RequestUtil.DoSomething getSearchBookPageList(Book book) {
        return ()-> {
            SpiderUtil spiderUtil = SpiderUtil.spiderUtilMap.get(book.getSpiderID());
            List<Page> pageList = new ArrayList<>();
            try {
                pageList = spiderUtil.searchBookPageList(book);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return pageList;
        };
    }

    /**
     * 生成搜章节内容事件
     * @param page 章节
     * @return 爬取事件
     */
    public static RequestUtil.DoSomething getSearchBookPage(Page page) {
        return ()-> {
            String result = "";
            SpiderUtil spiderUtil = spiderUtilMap.get(page.getSpiderID());
            try {
                result = spiderUtil.searchPage(page);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        };
    }

    /**
     * 搜书
     * @param name 书名
     * @return Book
     */
    public List<Book> searchBooks(String name) throws IOException {
        List<Book> books = new ArrayList<>();
        if (name == null) return books;

        Document doc = Jsoup.connect(spider.getSearchUrl() + name).userAgent(UA).timeout(3000).get();
        Elements elements = doc.select(spider.getBookSelect());
        if (elements.isEmpty()) return books;
        for (Element element:elements) {
            Book book = new Book();
            book.setSpiderID(spider.getSpiderID());
            book.setLink(spider.getHostUrl() + element.selectFirst(spider.getIndexUrlSelect()).attr("href"));
            book.setName(element.selectFirst(spider.getNameSelect()).html());
            book.setAuthor(element.selectFirst(spider.getAuthorSelect()).html());

            if (!spider.getLastPageSelect().equals("")){
                book.setLastPage(element.selectFirst(spider.getLastPageSelect()).html());
            }
            if (!spider.getImgSelect().equals("")){
                book.setImg(element.selectFirst(spider.getImgSelect()).attr("src"));
            }
            if (!spider.getInfoSelect().equals(""))
                book.setInfo(element.selectFirst(spider.getInfoSelect()).html());
            books.add(book);
        }
        return books;
    }

    /**
     * 更新书详情
     * @param book 书
     */
    public Book searchBookInfo(Book book) throws IOException {
        Document doc = Jsoup.connect(book.getLink()).userAgent(UA).timeout(3000).get();
        if (!spider.getInrImgSelect().equals(""))
            book.setImg(doc.selectFirst(spider.getInrImgSelect()).attr("src"));
        if (!spider.getInrInfoSelect().equals("")){
            if (doc.selectFirst(spider.getInrInfoSelect()) != null) {
                String info = doc.selectFirst(spider.getInrInfoSelect()).html()
                        .replace("<br>", "")
                        .replaceAll("<script.*?</script>", "")
                        .replaceAll("<div[\\s\\S]*?</div>", "")
                        .replaceAll("<[^>]+>", "")
                        .replace("\n", "")
                        .replace("&nbsp;", "")
                        .trim();
                book.setInfo(info);
            }
        }
        return book;
    }

    /**
     * 搜书章节
     * @param book 书
     */
    public List<Page> searchBookPageList(Book book) throws IOException {
        Document doc = Jsoup.connect(book.getLink()).userAgent(UA).timeout(3000).get();
        Elements elements = doc.select(spider.getPageSelect());
        int i = 0;
        List<Page> pageList = new ArrayList<>();
        for(Element element:elements) {
            Page page = new Page();
            page.setSpiderID(book.getSpiderID());
            page.setBookID(book.getBookID());
            page.setNum(i);
            page.setTitle(element.html());
            String url = (element.attr("href").charAt(0) == '/' ? spider.getHostUrl() : book.getLink() ) + element.attr("href");
            page.setLink(url);
            pageList.add(page);
            i++;
        }
        return pageList;
    }

    /**
     * 搜章节内容
     * @param page 章节
     */
    public String searchPage(Page page) throws IOException {
        Document doc = Jsoup.connect(page.getLink())
                .userAgent(UA)
                .timeout(10000)
                .get();

        return doc.selectFirst(spider.getContentSelect()).html()
                .replace("<br>", "\n")
                .replaceAll("<script.*?</script>", "")
                .replaceAll("<div[\\s\\S]*?</div>", "")
                .replaceAll("<[^>]+>", "")
                .replace("\n\n", "\n")
                .replace("&nbsp;", " ")
                .trim();
    }

    /**
     * 爬起点榜单事件
     */
    public static RequestUtil.DoSomething getSearchQiDian() {
        return ()-> {
            List<Book> result = null;
            try {
                result = searchQiDian();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        };
    }

    /**
     * 爬百度榜单事件
     */
    public static RequestUtil.DoSomething getSearchBaidu() {
        return ()-> {
            List<Book> result = null;
            try {
                result = searchBaidu();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        };
    }

    /**
     * 爬纵横榜单事件
     */
    public static RequestUtil.DoSomething getSearchZongheng() {
        return ()-> {
            List<Book> result = null;
            try {
                result = searchZongheng();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        };
    }

    /**
     * 爬起点榜单
     */
    public static List<Book> searchQiDian() throws IOException {
        Document document = Jsoup.connect("https://www.qidian.com/rank/yuepiao?style=1").get();
        Elements select = document.select(".book-img-text li");
        List<Book> result = new ArrayList<>();
        for (Element s:select) {
            Book book = new Book();
            book.setName(s.selectFirst("h4 a").html());
            book.setImg("http:" + s.selectFirst("img").attr("src"));
            book.setInfo(s.selectFirst(".intro").html());
            book.setAuthor(s.selectFirst(".author a").html());
            book.setLastPage(s.selectFirst(".update a").html());
            result.add(book);
        }
        return result;
    }

    /**
     * 爬百度榜单
     */
    public static List<Book> searchBaidu() throws IOException {
        Document document = Jsoup.connect("https://top.baidu.com/board?tab=novel").get();
        Elements select = document.select(".category-wrap_iQLoo");
        List<Book> result = new ArrayList<>();
        for (Element s:select) {
            Book book = new Book();
            book.setName(s.selectFirst(".c-single-text-ellipsis").html());
            book.setImg(s.selectFirst("img").attr("src"));
            book.setInfo(s.selectFirst(".desc_3CTjT").html());
            book.setAuthor(s.selectFirst(".intro_1l0wp").html());
            result.add(book);
        }
        return result;
    }

    /**
     * 爬纵横榜单
     */
    public static List<Book> searchZongheng() throws IOException {
        Document document = Jsoup.connect("http://www.zongheng.com/rank/details.html?rt=1&d=1").get();
        Elements select = document.select(".rank_d_list");
        List<Book> result = new ArrayList<>();
        for (Element s:select) {
            Book book = new Book();
            book.setName(s.selectFirst(".rank_d_b_name a").html());
            book.setImg(s.selectFirst("img").attr("src"));
            book.setInfo(s.selectFirst(".rank_d_b_info").html());
            book.setAuthor(s.selectFirst(".rank_d_b_cate a").html());
            book.setLastPage(s.selectFirst(".rank_d_b_last").attr("title"));
            result.add(book);
        }
        return result;
    }

    /**
     * 解析书名1
     */
    public static List<String> decodeName1(String result) {
        List<String> nameList = new ArrayList<>();
        ZSONObject zsonObject = ZSONObject.stringToZSON(result);
        ZSONArray array = zsonObject.getZSONArray("result");
        int size = array.size();
        for (int i=0; i<size; i++) {
            ZSONObject item = array.getZSONObject(i);
            nameList.add(item.getString("bookname"));
        }
        return nameList;
    }

    /**
     * 解析书名2
     */
    public static List<String> decodeName2(String result) {
        List<String> nameList = new ArrayList<>();
        Pattern p = Pattern.compile("Name>(.*?)</Name");
        Matcher m = p.matcher(result);
        boolean hasFind = m.find();
        if (hasFind) {
            while (hasFind) {
                String bookname = m.group(1);
                nameList.add(bookname);
                hasFind = m.find();
            }
        }
        return nameList;
    }

}
